package com.sishu.njrtsdms.utils;

import cn.hutool.core.collection.CollUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;

import java.io.*;
import java.util.List;

@Slf4j
public final class MyBeanUtils {

    /**
     * 浅拷贝
     * 将包含BeanA的list，转换为包含BeanB的list。应用场景示例：List<DO>转List<TO>
     *
     * @param sourceList
     * @param targetClass
     * @return
     */
    public static <T> List<T> copyList(List<?> sourceList,
                                       Class<T> targetClass) {
        return copyList(sourceList, targetClass, false);
    }

    /**
     * 浅拷贝
     * 将包含BeanA的list，转换为包含BeanB的list。应用场景示例：List<DO>转List<TO>
     *
     * @param sourceList
     * @param targetClass
     * @return
     */
    public static <T> List<T> copyList(List<?> sourceList,
                                       Class<T> targetClass,
                                       String... ignoreProperties) {
        return copyList(sourceList, targetClass, true, ignoreProperties);
    }

    /**
     * 浅拷贝
     * 将包含BeanA的list，转换为包含BeanB的list。应用场景示例：List<DO>转List<TO>
     *
     * @param sourceList
     * @param targetClass
     * @return
     */
    private static <T> List<T> copyList(List<?> sourceList,
                                        Class<T> targetClass,
                                        boolean needIgnoreProperties,
                                        String... ignoreProperties) {
        if (sourceList == null) {
            return null;
        }

        List<T> targetList = CollUtil.newArrayList();

        for (Object tempSrc : sourceList) {
            try {
                T t = copyBean(tempSrc, targetClass, needIgnoreProperties, ignoreProperties);
                targetList.add(t);
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage());
            }
        }

        return targetList;
    }

    /**
     * 浅拷贝,本质和Spring的BeanUtils一模一样
     * <p>
     * 默认拷贝方法,没有需要在拷贝中忽略的源属性
     *
     * @param srcBean     待转换Bean
     * @param targetClass 目标Bean的Class
     * @param <T>         目标Bean
     */
    public static <T> T copyBean(Object srcBean,
                                 Class<T> targetClass) {
        return copyBean(srcBean, targetClass, false);
    }

    /**
     * 浅拷贝,本质和Spring的BeanUtils一模一样
     * <p>
     * 默认拷贝方法,没有需要在拷贝中忽略的源属性
     *
     * @param srcBean     待转换Bean
     * @param targetClass 目标Bean的Class
     * @param <T>         目标Bean
     * @return
     */
    public static <T> T copyBean(Object srcBean,
                                 Class<T> targetClass,
                                 String... ignoreProperties) {
        return copyBean(srcBean, targetClass, true, ignoreProperties);
    }


    /**
     * 浅拷贝重载方法
     *
     * @param srcBean
     * @param targetClass
     * @param needIgnoreProperties
     * @param ignoreProperties
     * @param <T>
     * @return
     */
    private static <T> T copyBean(Object srcBean,
                                  Class<T> targetClass,
                                  boolean needIgnoreProperties,
                                  String... ignoreProperties) {
        try {
            T t = targetClass.getDeclaredConstructor().newInstance();
            if (!needIgnoreProperties) {
                BeanUtils.copyProperties(srcBean, t);
            } else {
                BeanUtils.copyProperties(srcBean, t, ignoreProperties);
            }
            return t;
        } catch (Exception e) {
            log.error("copyBean failed");
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 深拷贝
     *
     * @param src
     * @param <T>
     * @return
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static <T> List<T> deepCopy(List<T> src) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(byteOut);
        out.writeObject(src);

        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
        ObjectInputStream in = new ObjectInputStream(byteIn);
        @SuppressWarnings("unchecked")
        List<T> dest = (List<T>) in.readObject();
        return dest;
    }
}